LCOV - code coverage report
Current view: top level - src - sock.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 3.8 % 159 6
Test Date: 2024-06-28 00:00:00 Functions: 16.7 % 18 3

            Line data    Source code
       1              : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
       2              : /* sock.c
       3              : ** strophe XMPP client library -- socket abstraction implementation
       4              : **
       5              : ** Copyright (C) 2005-2009 Collecta, Inc.
       6              : **
       7              : **  This software is provided AS-IS with no warranty, either express
       8              : **  or implied.
       9              : **
      10              : ** This program is dual licensed under the MIT or GPLv3 licenses.
      11              : */
      12              : 
      13              : /** @file
      14              :  *  Socket abstraction.
      15              :  */
      16              : 
      17              : #include <stdio.h>
      18              : #include <stdlib.h>
      19              : #include <string.h>
      20              : #include <sys/types.h>
      21              : 
      22              : #ifdef _WIN32
      23              : #include <winsock2.h>
      24              : #include <ws2tcpip.h>
      25              : #include <iphlpapi.h>
      26              : #include <mstcpip.h> /* tcp_keepalive */
      27              : #else
      28              : #include <arpa/inet.h>
      29              : #include <errno.h>
      30              : #include <unistd.h>
      31              : #include <sys/socket.h>
      32              : #include <netinet/in.h>
      33              : #include <netinet/tcp.h>
      34              : #include <netdb.h>
      35              : #include <fcntl.h>
      36              : #endif
      37              : 
      38              : #include "common.h"
      39              : #include "resolver.h"
      40              : 
      41              : const struct conn_interface sock_intf = {
      42              :     sock_read,
      43              :     sock_write,
      44              :     /* no flush */
      45              :     conn_int_nop,
      46              :     /* no pending */
      47              :     conn_int_nop,
      48              :     sock_error,
      49              :     sock_is_recoverable,
      50              :     NULL,
      51              : };
      52              : 
      53              : struct _xmpp_sock_t {
      54              :     xmpp_ctx_t *ctx;
      55              :     xmpp_conn_t *conn;
      56              :     struct addrinfo *ainfo_list;
      57              :     struct addrinfo *ainfo_cur;
      58              :     resolver_srv_rr_t *srv_rr_list;
      59              :     resolver_srv_rr_t *srv_rr_cur;
      60              :     const char *host;
      61              :     unsigned short port;
      62              : };
      63              : 
      64            2 : void sock_initialize(void)
      65              : {
      66              : #ifdef _WIN32
      67              :     WSADATA wsad;
      68              :     WSAStartup(0x0101, &wsad);
      69              : #endif
      70            2 : }
      71              : 
      72            2 : void sock_shutdown(void)
      73              : {
      74              : #ifdef _WIN32
      75              :     WSACleanup();
      76              : #endif
      77            2 : }
      78              : 
      79            0 : int sock_error(struct conn_interface *intf)
      80              : {
      81            0 :     UNUSED(intf);
      82              : #ifdef _WIN32
      83              :     return WSAGetLastError();
      84              : #else
      85            0 :     return errno;
      86              : #endif
      87              : }
      88              : 
      89              : static int _in_progress(int error)
      90              : {
      91              : #ifdef _WIN32
      92              :     return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
      93              : #else
      94              :     return (error == EINPROGRESS);
      95              : #endif
      96              : }
      97              : 
      98            0 : static void sock_getaddrinfo(xmpp_sock_t *xsock)
      99              : {
     100            0 :     char service[6];
     101            0 :     struct addrinfo hints;
     102            0 :     int rc;
     103              : 
     104            0 :     if (xsock->ainfo_list) {
     105            0 :         freeaddrinfo(xsock->ainfo_list);
     106            0 :         xsock->ainfo_list = NULL;
     107              :     }
     108              : 
     109            0 :     if (xsock->srv_rr_cur) {
     110              :         /* Cache host and port for debug logs. */
     111            0 :         xsock->host = xsock->srv_rr_cur->target;
     112            0 :         xsock->port = xsock->srv_rr_cur->port;
     113              : 
     114            0 :         strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
     115            0 :         memset(&hints, 0, sizeof(struct addrinfo));
     116            0 :         hints.ai_family = AF_UNSPEC;
     117              : #ifdef AI_ADDRCONFIG
     118            0 :         hints.ai_flags = AI_ADDRCONFIG;
     119              : #endif /* AI_ADDRCONFIG */
     120            0 :         hints.ai_protocol = IPPROTO_TCP;
     121            0 :         hints.ai_socktype = SOCK_STREAM;
     122              : 
     123            0 :         rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
     124              :                          &xsock->ainfo_list);
     125            0 :         if (rc != 0) {
     126            0 :             strophe_debug(xsock->ctx, "sock",
     127              :                           "getaddrinfo() failed with %s (%d)", gai_strerror(rc),
     128              :                           rc);
     129            0 :             xsock->ainfo_list = NULL;
     130              :         }
     131              :     }
     132              : 
     133            0 :     xsock->ainfo_cur = xsock->ainfo_list;
     134            0 : }
     135              : 
     136            0 : xmpp_sock_t *sock_new(xmpp_conn_t *conn,
     137              :                       const char *domain,
     138              :                       const char *host,
     139              :                       unsigned short port)
     140              : {
     141            0 :     xmpp_ctx_t *ctx = conn->ctx;
     142            0 :     xmpp_sock_t *xsock;
     143            0 :     int found = XMPP_DOMAIN_NOT_FOUND;
     144              : 
     145            0 :     xsock = strophe_alloc(ctx, sizeof(*xsock));
     146            0 :     if (!xsock) {
     147              :         return NULL;
     148              :     }
     149              : 
     150            0 :     xsock->ctx = ctx;
     151            0 :     xsock->conn = conn;
     152            0 :     xsock->host = NULL;
     153            0 :     xsock->port = 0;
     154              : 
     155            0 :     if (!host) {
     156            0 :         found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
     157              :                                     &xsock->srv_rr_list);
     158            0 :         if (XMPP_DOMAIN_NOT_FOUND == found)
     159            0 :             strophe_debug(ctx, "sock",
     160              :                           "SRV lookup failed, connecting via domain.");
     161              :     }
     162            0 :     if (XMPP_DOMAIN_NOT_FOUND == found) {
     163              :         /* Resolution failed or the host is provided explicitly. */
     164            0 :         xsock->srv_rr_list =
     165            0 :             resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
     166              :     }
     167            0 :     xsock->srv_rr_cur = xsock->srv_rr_list;
     168              : 
     169            0 :     xsock->ainfo_list = NULL;
     170            0 :     sock_getaddrinfo(xsock);
     171            0 :     if (xsock->srv_rr_cur)
     172            0 :         xsock->srv_rr_cur = xsock->srv_rr_cur->next;
     173              : 
     174              :     return xsock;
     175              : }
     176              : 
     177            8 : void sock_free(xmpp_sock_t *xsock)
     178              : {
     179            8 :     if (!xsock)
     180              :         return;
     181              : 
     182            0 :     if (xsock->ainfo_list)
     183            0 :         freeaddrinfo(xsock->ainfo_list);
     184            0 :     if (xsock->srv_rr_list)
     185            0 :         resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
     186            0 :     strophe_free(xsock->ctx, xsock);
     187              : }
     188              : 
     189            0 : static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
     190              : {
     191            0 :     buf[0] = '\0';
     192              : 
     193            0 :     switch (sa->sa_family) {
     194            0 :     case AF_INET:
     195            0 :         inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
     196            0 :         break;
     197            0 :     case AF_INET6:
     198            0 :         inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
     199              :                   buflen);
     200            0 :         break;
     201              :     default:
     202            0 :         strophe_snprintf(buf, buflen, "<Unknown>");
     203              :     }
     204            0 :     return buf;
     205              : }
     206              : 
     207            0 : sock_t sock_connect(xmpp_sock_t *xsock)
     208              : {
     209            0 :     struct addrinfo *ainfo;
     210            0 :     sock_t sock;
     211            0 :     int rc;
     212            0 :     char buf[64];
     213              : 
     214            0 :     do {
     215            0 :         if (!xsock->ainfo_cur) {
     216            0 :             sock_getaddrinfo(xsock);
     217            0 :             if (xsock->srv_rr_cur)
     218            0 :                 xsock->srv_rr_cur = xsock->srv_rr_cur->next;
     219              :         }
     220            0 :         if (!xsock->ainfo_cur) {
     221              :             /* We tried all available addresses. */
     222            0 :             return INVALID_SOCKET;
     223              :         }
     224              : 
     225            0 :         ainfo = xsock->ainfo_cur;
     226            0 :         strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
     227            0 :                       xsock->host, xsock->port,
     228              :                       _sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
     229              : 
     230            0 :         sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
     231            0 :         if (sock != INVALID_SOCKET) {
     232            0 :             rc = 0;
     233            0 :             if (xsock->conn->sockopt_cb) {
     234              :                 /* Don't allow user to overwrite sockfd value. */
     235            0 :                 sock_t sock_copy = sock;
     236            0 :                 rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
     237            0 :                 if (rc != 0) {
     238            0 :                     strophe_debug(xsock->ctx, "sock",
     239              :                                   "User's setsockopt callback"
     240              :                                   " failed with %d (errno=%d)",
     241            0 :                                   rc, errno);
     242              :                 }
     243              :             }
     244            0 :             if (rc == 0)
     245            0 :                 rc = sock_set_nonblocking(sock);
     246            0 :             if (rc == 0)
     247            0 :                 rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
     248              :             /* Assume only connect() can cause "in progress" error. */
     249            0 :             if (rc != 0 && !_in_progress(sock_error(NULL))) {
     250            0 :                 sock_close(sock);
     251              :                 sock = INVALID_SOCKET;
     252              :             }
     253              :         }
     254            0 :         strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
     255              : 
     256            0 :         xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
     257            0 :     } while (sock == INVALID_SOCKET);
     258              : 
     259              :     return sock;
     260              : }
     261              : 
     262            0 : int sock_set_keepalive(sock_t sock,
     263              :                        int timeout,
     264              :                        int interval,
     265              :                        int count,
     266              :                        unsigned int user_timeout)
     267              : {
     268            0 :     int ret;
     269            0 :     int optval = (timeout && interval) ? 1 : 0;
     270              : 
     271            0 :     UNUSED(count);
     272            0 :     UNUSED(user_timeout);
     273              : 
     274              : #ifdef _WIN32
     275              :     struct tcp_keepalive ka;
     276              :     DWORD dw = 0;
     277              : 
     278              :     ka.onoff = optval;
     279              :     ka.keepalivetime = timeout * 1000;
     280              :     ka.keepaliveinterval = interval * 1000;
     281              :     ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
     282              :                    NULL, NULL);
     283              : #else
     284            0 :     ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
     285            0 :     if (ret < 0)
     286              :         return ret;
     287              : 
     288            0 :     if (optval) {
     289              : #ifdef TCP_KEEPIDLE
     290            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
     291              :                          sizeof(timeout));
     292              : #elif defined(TCP_KEEPALIVE)
     293              :         /* QNX receives `struct timeval' as argument, but it seems OSX does int
     294              :          */
     295              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
     296              :                          sizeof(timeout));
     297              : #endif /* TCP_KEEPIDLE */
     298            0 :         if (ret < 0)
     299              :             return ret;
     300              : #ifdef TCP_KEEPINTVL
     301            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
     302              :                          sizeof(interval));
     303            0 :         if (ret < 0)
     304              :             return ret;
     305              : #endif /* TCP_KEEPINTVL */
     306              :     }
     307              : 
     308            0 :     if (count) {
     309              : #ifdef TCP_KEEPCNT
     310            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
     311            0 :         if (ret < 0)
     312              :             return ret;
     313              : #endif /* TCP_KEEPCNT */
     314              :     }
     315              : 
     316            0 :     if (user_timeout) {
     317              : #ifdef TCP_USER_TIMEOUT
     318            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
     319              :                          sizeof(user_timeout));
     320            0 :         if (ret < 0)
     321              :             return ret;
     322              : #elif defined(TCP_RXT_CONNDROPTIME)
     323              :         int rxt = user_timeout / 1000;
     324              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
     325              :                          sizeof(rxt));
     326              :         if (ret < 0)
     327              :             return ret;
     328              : #endif /* TCP_USER_TIMEOUT */
     329              :     }
     330              : 
     331              : #endif /* _WIN32 */
     332              : 
     333              :     return ret;
     334              : }
     335              : 
     336              : /** Example sockopt callback function
     337              :  *  An example function that can be used to set reasonable default keepalive
     338              :  *  options on sockets when registered for a connection with
     339              :  *  xmpp_conn_set_sockopt_callback()
     340              :  *
     341              :  *  @param conn a Strophe connection object
     342              :  *  @param socket pointer to a socket descriptor
     343              :  *
     344              :  *  @see xmpp_sockopt_callback for details on the `socket` parameter
     345              :  *  @ingroup Connections
     346              :  */
     347            0 : int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
     348              : {
     349            0 :     sock_t sock = *((sock_t *)socket);
     350              : 
     351            0 :     return sock_set_keepalive(
     352              :         sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
     353            0 :         conn->ka_count
     354            0 :             ? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
     355              :             : 0);
     356              : }
     357              : 
     358            0 : int sock_close(sock_t sock)
     359              : {
     360              : #ifdef _WIN32
     361              :     return closesocket(sock);
     362              : #else
     363            0 :     return close(sock);
     364              : #endif
     365              : }
     366              : 
     367            0 : static int _sock_set_blocking_mode(sock_t sock, int blocking)
     368              : {
     369              : #ifdef _WIN32
     370              :     u_long nonblock = blocking ? 0 : 1;
     371              :     return ioctlsocket(sock, FIONBIO, &nonblock);
     372              : #else
     373            0 :     int rc;
     374              : 
     375            0 :     rc = fcntl(sock, F_GETFL, NULL);
     376            0 :     if (rc >= 0) {
     377            0 :         rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
     378            0 :         rc = fcntl(sock, F_SETFL, rc);
     379              :     }
     380            0 :     return rc;
     381              : #endif
     382              : }
     383              : 
     384            0 : int sock_set_blocking(sock_t sock)
     385              : {
     386            0 :     return _sock_set_blocking_mode(sock, 1);
     387              : }
     388              : 
     389            0 : int sock_set_nonblocking(sock_t sock)
     390              : {
     391            0 :     return _sock_set_blocking_mode(sock, 0);
     392              : }
     393              : 
     394            0 : int sock_read(struct conn_interface *intf, void *buff, size_t len)
     395              : {
     396            0 :     return recv(intf->conn->sock, buff, len, 0);
     397              : }
     398              : 
     399            0 : int sock_write(struct conn_interface *intf, const void *buff, size_t len)
     400              : {
     401            0 :     return send(intf->conn->sock, buff, len, 0);
     402              : }
     403              : 
     404            0 : int sock_is_recoverable(struct conn_interface *intf, int error)
     405              : {
     406            0 :     UNUSED(intf);
     407              : #ifdef _WIN32
     408              :     return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
     409              :             error == WSAEINPROGRESS);
     410              : #else
     411            0 :     return (error == EAGAIN || error == EINTR);
     412              : #endif
     413              : }
     414              : 
     415            0 : int sock_connect_error(sock_t sock)
     416              : {
     417            0 :     struct sockaddr_storage ss;
     418            0 :     struct sockaddr *sa = (struct sockaddr *)&ss;
     419            0 :     socklen_t len;
     420            0 :     char temp;
     421              : 
     422            0 :     memset(&ss, 0, sizeof(ss));
     423            0 :     len = sizeof(ss);
     424            0 :     sa->sa_family = AF_UNSPEC;
     425              : 
     426              :     /* we don't actually care about the peer name, we're just checking if
     427              :      * we're connected or not */
     428            0 :     if (getpeername(sock, sa, &len) == 0) {
     429            0 :         return 0;
     430              :     }
     431              : 
     432              :     /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
     433              :      * return that */
     434              : #ifdef _WIN32
     435              :     if (sock_error(NULL) != WSAENOTCONN)
     436              :         return sock_error(NULL);
     437              : #else
     438            0 :     if (sock_error(NULL) != ENOTCONN)
     439            0 :         return sock_error(NULL);
     440              : #endif
     441              : 
     442              :     /* load the correct error into errno through error slippage */
     443            0 :     recv(sock, &temp, 1, 0);
     444              : 
     445            0 :     return sock_error(NULL);
     446              : }
        

Generated by: LCOV version 2.0-1